builtinicon: avoid calculating font-metrics in vast majority of cases
authorChristian Hergert <chergert@redhat.com>
Mon, 25 Apr 2016 07:22:54 +0000 (00:22 -0700)
committerMatthias Clasen <mclasen@redhat.com>
Mon, 25 Apr 2016 20:13:06 +0000 (16:13 -0400)
We perform lots of gadget allocations that require allocating a
GtkBuiltinIcon. One notable example is the scrollbar for a scrolled
window.

In the process of doing this, we often calculate baseline information that
isn't necessary. With how much this code path gets exercised, its worth
catching the result for the common case, which is that the font-description
has not changed and we are using the default language the application
was started with.

This simply caches the previous result and verifies that we can reuse it
with pango_font_description_hash() and a simple language check.

Numbers below are scrolling through a textview with GDK_KEY_Down.

Before:
      SELF CUMULATIVE    FUNCTION
[   0.08%] [   9.26%]    gtk_builtin_icon_get_preferred_size
[   0.01%] [   8.82%]      pango_context_get_metrics
[   0.02%] [   0.16%]      gtk_widget_get_pango_context
[   0.06%] [   0.06%]      pango_context_get_language
[   0.01%] [   0.02%]      g_type_check_instance_cast
[   0.02%] [   0.02%]      strlen
[   0.02%] [   0.02%]      pango_context_get_font_description
[   0.02%] [   0.02%]      g_list_foreach
[   0.01%] [   0.01%]      gtk_css_style_get_value
[   0.01%] [   0.01%]      itemize_with_font
[   0.01%] [   0.01%]      pango_context_get_type
[   0.01%] [   0.01%]      get_base_metrics
[   0.00%] [   0.01%]      pango_font_metrics_unref
[   0.01%] [   0.01%]      g_list_free
[   0.01%] [   0.01%]      gtk_builtin_icon_get_type

After:
      SELF CUMULATIVE    FUNCTION
[   0.08%] [   0.18%]    gtk_builtin_icon_get_preferred_size
[   0.02%] [   0.02%]      pango_font_description_hash
[   0.00%] [   0.02%]      gtk_widget_get_pango_context
[   0.00%] [   0.02%]        g_object_get_qdata
[   0.00%] [   0.02%]          g_datalist_id_get_data
[   0.02%] [   0.02%]      gtk_builtin_icon_get_type
[   0.01%] [   0.01%]      pango_context_get_font_description
[   0.00%] [   0.01%]      - - kernel - -
[   0.01%] [   0.01%]      pango_context_get_language
[   0.00%] [   0.01%]      gtk_css_style_get_value
[   0.00%] [   0.01%]      gtk_css_gadget_get_style

https://bugzilla.gnome.org/show_bug.cgi?id=765486

gtk/gtkbuiltinicon.c

index 8b9957344605f6d24a8d6b8befed44a257db6bde..feedb2cbf80f8c02313747c90ce7dfead99037d4 100644 (file)
@@ -47,6 +47,8 @@ typedef struct _GtkBuiltinIconPrivate GtkBuiltinIconPrivate;
 struct _GtkBuiltinIconPrivate {
   GtkCssImageBuiltinType        image_type;
   int                           default_size;
+  int                           strikethrough;
+  gboolean                      strikethrough_valid;
   char *                        default_size_property;
 };
 
@@ -63,10 +65,6 @@ gtk_builtin_icon_get_preferred_size (GtkCssGadget   *gadget,
                                      gint           *natural_baseline)
 {
   GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (gadget));
-  GtkWidget *widget;
-  PangoContext *pango_context;
-  PangoFontMetrics *metrics;
-  int strikethrough;
   double min_size;
   guint property;
 
@@ -98,21 +96,33 @@ gtk_builtin_icon_get_preferred_size (GtkCssGadget   *gadget,
       *minimum = *natural = priv->default_size;
     }
 
-  widget = gtk_css_gadget_get_owner (gadget);
-
-  pango_context = gtk_widget_get_pango_context (widget);
-  metrics = pango_context_get_metrics (pango_context,
-                                       pango_context_get_font_description (pango_context),
-                                       pango_context_get_language (pango_context));
-
-  strikethrough = pango_font_metrics_get_strikethrough_position (metrics);
-
   if (minimum_baseline)
-    *minimum_baseline = *minimum * 0.5 + PANGO_PIXELS (strikethrough);
+    {
+      if (!priv->strikethrough_valid)
+        {
+          GtkWidget *widget;
+          PangoContext *pango_context;
+          const PangoFontDescription *font_desc;
+          PangoFontMetrics *metrics;
+
+          widget = gtk_css_gadget_get_owner (gadget);
+          pango_context = gtk_widget_get_pango_context (widget);
+          font_desc = pango_context_get_font_description (pango_context);
+
+          metrics = pango_context_get_metrics (pango_context,
+                                               font_desc,
+                                               pango_context_get_language (pango_context));
+          priv->strikethrough = pango_font_metrics_get_strikethrough_position (metrics);
+          priv->strikethrough_valid = TRUE;
+
+          pango_font_metrics_unref (metrics);
+        }
+
+      *minimum_baseline = *minimum * 0.5 + PANGO_PIXELS (priv->strikethrough);
+    }
+
   if (natural_baseline)
     *natural_baseline = *minimum_baseline;
-
-  pango_font_metrics_unref (metrics);
 }
 
 static void
@@ -151,6 +161,18 @@ gtk_builtin_icon_draw (GtkCssGadget *gadget,
   return FALSE;
 }
 
+static void
+gtk_builtin_icon_style_changed (GtkCssGadget      *gadget,
+                                GtkCssStyleChange *change)
+{
+  GtkBuiltinIconPrivate *priv = gtk_builtin_icon_get_instance_private (GTK_BUILTIN_ICON (gadget));
+
+  if (gtk_css_style_change_affects (change, GTK_CSS_AFFECTS_FONT))
+    priv->strikethrough_valid = FALSE;
+
+  GTK_CSS_GADGET_CLASS (gtk_builtin_icon_parent_class)->style_changed (gadget, change);
+}
+
 static void
 gtk_builtin_icon_finalize (GObject *object)
 {
@@ -172,6 +194,7 @@ gtk_builtin_icon_class_init (GtkBuiltinIconClass *klass)
   gadget_class->get_preferred_size = gtk_builtin_icon_get_preferred_size;
   gadget_class->allocate = gtk_builtin_icon_allocate;
   gadget_class->draw = gtk_builtin_icon_draw;
+  gadget_class->style_changed = gtk_builtin_icon_style_changed;
 }
 
 static void